package com.apes.scm.invoice.model;

import com.alibaba.fastjson.annotation.JSONField;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.DefaultPersistListener;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.comment.Comment;
import com.apes.framework.jpa.generator.ApesGenerator;
import com.apes.framework.plugin.soe.annotations.State;
import com.apes.framework.plugin.soe.annotations.States;
import com.apes.framework.plugin.soe.metadata.BusinessType;
import com.apes.framework.plugin.soe.store.database.repository.BusinessTypeRepository;
import com.apes.framework.util.ApesConst;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.LogicalUtil;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.party.model.PartyRole;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;

import javax.persistence.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * 功能：账单
 *
 * @author xul
 * @create 2018-01-27 11:10
 */
@Getter
@Setter
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Entity
@EntityListeners(DefaultPersistListener.class)
@Table(name = Invoice.CONST_TABLE_NAME,
        indexes = {
                @Index(name = "idx_contract_id", columnList = "contract_id"),
                @Index(name = "idx_partner_company_id", columnList = "partner_id,company_id")
        })
public class Invoice extends Model {
    public static final String CONST_TABLE_NAME = "acc_invoice";

    @Id
    @Column(name = "id", length = 32)
    @ApesGenerator(code = CONST_TABLE_NAME, name = "账单", padding = 5, useDateRange=true)
    @Comment("单号")
    private long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "company_id", referencedColumnName = "id", nullable = false)
    @Comment("公司编码")
    private PartyRole company;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "dept_id", referencedColumnName = "id", nullable = false)
    @Comment("部门编码")
    private Dept dept;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "partner_id", referencedColumnName = "id", nullable = false)
    @Comment("合作伙伴编码")
    private PartyRole partner;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "contract_id", referencedColumnName = "id")
    @Comment("合同编码")
    private Contract contract;

    /**
     * 库存方式
     * 1. 适用于：经销
     * IM_OWN: 自有
     * 2. 适用于：代销、联营
     * IM_CONSIGN: 寄售
     */
    @States(name = "库存方式", values = {
            @State(id = InventoryMode.IM_OWN, name = "自有"),
            @State(id = InventoryMode.IM_CONSIGN, name = "寄售")})
    @Column(name = "inventory_mode", nullable = false, length = 20)
    @Comment("库存方式")
    private String inventoryMode;

    public void setInventoryMode(String inventoryMode) {
        if (LogicalUtil.notIn(inventoryMode, InventoryMode.IM_OWN, InventoryMode.IM_CONSIGN)) {
            throw new RuntimeException("库存方式输入错误！");
        }
        this.inventoryMode = inventoryMode;
    }

    /**
     * 采购核算方式
     * 1. 适用于：经销、代销
     * AM_PURCHASE: 按采购价核算
     * 2. 适用于：联营
     * AM_SALE: 按售价（或码价）核算
     */
    @States(name = "采购核算方式", values = {
            @State(id = AccountingMethod.AM_PURCHASE, name = "按采购价核算"),
            @State(id = AccountingMethod.AM_SALE, name = "按售价（或码价）核算"),
            @State(id = AccountingMethod.AM_REAL, name = "按实时定价核算")})
    @Column(name = "accounting_method", nullable = false, length = 20)
    @Comment("采购核算方式")
    private String accountingMethod;

    public void setAccountingMethod(String accountingMethod) {
        if (LogicalUtil.notIn(accountingMethod, AccountingMethod.AM_PURCHASE, AccountingMethod.AM_SALE, AccountingMethod.AM_REAL)) {
            throw new RuntimeException("核算方式输入错误！");
        }
        this.accountingMethod = accountingMethod;
    }

    @Column(name = "origin_business_type", length = 32, nullable = false)
    @Comment("源单据类型")
    private String originBusinessType;

    @Column(name = "origin", length = 30)
    @Comment("源单据号码")
    private String origin;

    @Column(name = "source_business_type", length = 32, nullable = false)
    @Comment("来源单据类型")
    private String sourceBusinessType;

    @Column(name = "source", length = 30)
    @Comment("来源单据号码")
    private String source;

    @Column(name = "classification", length = 30)
    @Comment("凭证分类")
    private String classification;

    @Column(name = "amt_total", nullable = false, columnDefinition = ApesConst.FIELD_16_4)
    @Comment("总计")
    private double amtTotal = 0;

    @Column(name = "amt_tax", nullable = false, columnDefinition = ApesConst.FIELD_16_4)
    @Comment("税金")
    private double amtTax = 0;

    @Column(name = "amt_untaxed", nullable = false, columnDefinition = ApesConst.FIELD_16_4)
    @Comment("未含税金额")
    private double amtUntaxed = 0;

    @Column(name = "discount", nullable = false, columnDefinition = ApesConst.FIELD_16_4)
    @Comment("折扣金额")
    private double discount = 0;

    @Column(name = "remark", length = 250)
    @Comment("备注")
    private String remark;

    @Column(name = "date_account")
    @Comment("会计日期")
    private Date dateAccount = new Date();

    @Column(name = "date_invoice")
    @Comment("开票日期")
    private Date dateInvoice = new Date();

    @Column(name = "has_outstanding")
    @Comment("有未完结的")
    private boolean hasOutstanding = true;

    @Comment("会员扩展信息编码")
    @Column(name = "customer_expand_id", length = 32)
    private String customerExpand;

    @Column(name = "state", nullable = false, columnDefinition = ApesConst.FIELD_16_4_0)
    @Comment("状态（0：有效；1：冻结；2：冲销）")
    private int state = 0;

    @JSONField(format = DateUtil.SHOW_DATETIME_FORMAT)
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "third_time")
    @Comment("第三方时间")
    private Date thirdTime;

    @Column(name = "third_id", length = 30)
    @Comment("第三方单号")
    private String thirdId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "modified_uid", referencedColumnName = "id")
    @Comment("账单日期修改人")
    private PartyRole modifiedUid;

    @Column(name = "modified_date")
    @Comment("最后账单修改日期")
    private Date modifiedDate;

    public void setState(int state) {
        if (this.state == 2) {
            throw new RuntimeException("账单已无效，不允许状态变更！");
        }
        if (state == 1 && this.state != 0) {
            throw new RuntimeException("账单已冲销，不允许冻结！");
        }
        this.state = state;
    }

    /**
     * 冻结
     **/
    public void freeze() {
        setState(1);
    }

    /**
     * 解冻
     */
    public void thaw(){
        if (state == 1) {
            setState(0);
        }
    }
    /**
     * 检查帐单是否允许冲销
     **/
    public void checkWriteOff() {
        if (DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT) != DateUtil.dateToDate(this.getDateAccount(), DateUtil.SHOW_DATE_FORMAT)) {
            throw new RuntimeException("非当天帐单，不允许冲销！");
        }
        if (this.getInvoiceItems().stream().filter(item -> item.getQty() != item.getPendingQty()).count() > 0) {
            throw new RuntimeException("账单已有结算数据，不允许冲销！");
        }
        if (this.getState() == 1) {
            throw new RuntimeException("账单已冻结，不允许冲销！");
        }
        if (this.getState() == 2) {
            throw new RuntimeException("账单已冲销，不允许重复冲销！");
        }
    }

    @OneToMany(mappedBy = "invoice", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<InvoiceItem> invoiceItems = new HashSet(); //发票行

    public Set<InvoiceItem> getInvoiceItems() {
        if(invoiceItems == null) return new HashSet<>();
        for (InvoiceItem item : invoiceItems) {
            item.setInvoice(this);
            item.setCompany(this.getCompany());
        }
        return invoiceItems;
    }

    public void computeAmt() {
        amtUntaxed = 0;
        amtTax = 0;
        discount = 0;
        for (InvoiceItem item : getInvoiceItems()) {
            item.computeAmt();
            amtUntaxed += item.getAmtUntaxed();
            amtTax += item.getAmtTax();
            discount += item.getDiscount();
        }
        amtTotal = amtUntaxed + amtTax;
    }

    @Transient
    private String originBusinessTypeName; //源单据类型

    public String getOriginBusinessTypeName() {
        return getBusinessTypeName("originBusinessType");
    }

    @Transient
    private String sourceBusinessTypeName; //来源单据类型

    public String getSourceBusinessTypeName() {
        return getBusinessTypeName("sourceBusinessType");
    }
}
